mod bounds;
mod cast;
mod identities;
pub mod image;
mod num;
use super::cast::As;
use super::types::Color;
use std::cmp::max;

pub trait Pixel: Copy + Sized {
  type Image: Sized + 'static;

  type Canvas: Canvas<Pixel = Self, Image = Self::Image>;
  fn default_unit_size() -> (u32, u32) {
    (8, 8)
  }
  fn default_color(color: Color) -> Self;
}

pub trait Canvas: Sized {
  type Pixel: Sized;
  type Image: Sized;

  fn new(
    width: u32,
    height: u32,
    dark_pixel: Self::Pixel,
    light_pixel: Self::Pixel,
  ) -> Self;

  fn draw_dark_pixel(&mut self, x: u32, y: u32);

  fn draw_dark_rect(&mut self, left: u32, top: u32, width: u32, height: u32) {
    for y in top..(top + height) {
      for x in left..(left + width) {
        self.draw_dark_pixel(x, y);
      }
    }
  }

  fn into_image(self) -> Self::Image;
}

pub struct Renderer<'a, P: Pixel> {
  content: &'a [Color],
  modules_count: u32,
  quiet_zone: u32,
  module_size: (u32, u32),
  dark_color: P,
  light_color: P,
  has_quiet_zone: bool,
}

impl<'a, P: Pixel> Renderer<'a, P> {
  pub fn new(
    content: &'a [Color],
    modules_count: usize,
    quiet_zone: u32,
  ) -> Renderer<'a, P> {
    assert!(modules_count * modules_count == content.len());
    Renderer {
      content,
      modules_count: modules_count.as_u32(),
      quiet_zone,
      module_size: P::default_unit_size(),
      dark_color: P::default_color(Color::Dark),
      light_color: P::default_color(Color::Light),
      has_quiet_zone: true,
    }
  }

  pub fn dark_color(&mut self, color: P) -> &mut Self {
    self.dark_color = color;
    self
  }

  pub fn light_color(&mut self, color: P) -> &mut Self {
    self.light_color = color;
    self
  }
  pub fn quiet_zone(&mut self, has_quiet_zone: bool) -> &mut Self {
    self.has_quiet_zone = has_quiet_zone;
    self
  }

  pub fn module_dimensions(&mut self, width: u32, height: u32) -> &mut Self {
    self.module_size = (max(width, 1), max(height, 1));
    self
  }
  pub fn min_dimensions(&mut self, width: u32, height: u32) -> &mut Self {
    let quiet_zone = if self.has_quiet_zone { 2 } else { 0 } * self.quiet_zone;
    let width_in_modules = self.modules_count + quiet_zone;
    let unit_width = (width + width_in_modules - 1) / width_in_modules;
    let unit_height = (height + width_in_modules - 1) / width_in_modules;
    self.module_dimensions(unit_width, unit_height)
  }

  pub fn max_dimensions(&mut self, width: u32, height: u32) -> &mut Self {
    let quiet_zone = if self.has_quiet_zone { 2 } else { 0 } * self.quiet_zone;
    let width_in_modules = self.modules_count + quiet_zone;
    let w = width / width_in_modules;
    let h = height / width_in_modules;
    self.module_dimensions(w, h)
  }

  pub fn build(&self) -> P::Image {
    let w = self.modules_count;
    let qz = if self.has_quiet_zone {
      self.quiet_zone
    } else {
      0
    };
    let width = w + 2 * qz;
    let (mw, mh) = self.module_size;
    let real_width = width * mw;
    let read_height = width * mh;
    let mut canvas = P::Canvas::new(
      real_width,
      read_height,
      self.dark_color,
      self.light_color,
    );
    let mut i = 0;
    for y in 0..width {
      for x in 0..width {
        if qz <= x && x < w + qz && qz <= y && y < w + qz {
          if self.content[i] != Color::Light {
            canvas.draw_dark_rect(x * mw, y * mh, mw, mh);
          }
          i += 1;
        }
      }
    }
    canvas.into_image()
  }
}
